﻿#include <mzfc_inc.h>
#include <InitGuid.h>
#include <IMzUnknown.h>
#include <IMzUnknown_IID.h>
#include <IFileBrowser.h>
#include <IFileBrowser_GUID.h>
#include <IPhotoViewer.h>
#include <IPhotoViewer_GUID.h>
#include <IMzSysFile.h>
#include <ShellNotifyMsg.h>
#include <ReadWriteIni.h>

#include "resource.h"

#define OVIEW_APP_NAME      L"oView"

#define SCREEN_ROTATION_0   1
#define SCREEN_ROTATION_90  0

#define MZ_IDC_OVIEW_CANVAS 101
#define MZ_IDC_TOOLBARICON  102

#define MZ_IDC_TOOLBARICON_NAVI  103

#define TIMER_FULLSCREEN_PRESSED 201

typedef std::map<std::wstring, int> FilesInDir;

CMzString AppPath;
DWORD AppRotation = SCREEN_ROTATION_0;


BOOL RotateScreen(DWORD dwRotaion)
{
    if (dwRotaion == 0xFFFFFF)
    {
        return false;
    }

    DEVMODE settings;
    memset(&settings, 0, sizeof(DEVMODE));
    settings.dmSize = sizeof(DEVMODE);
    settings.dmFields = DM_DISPLAYORIENTATION;
    ChangeDisplaySettingsEx(NULL, &settings, NULL, CDS_TEST, NULL);
    DWORD rotation = settings.dmDisplayOrientation;

    if (dwRotaion == rotation) return false;
    settings.dmDisplayOrientation = dwRotaion;
    return (DISP_CHANGE_SUCCESSFUL == ChangeDisplaySettingsEx(NULL, &settings, NULL, 0, NULL));
}

// 从SDK提供的 CMzWndEx 派生出自己的窗体类
class COViewWnd: public CMzWndEx
{
	// SDK提供的一个宏，用于自动生成返回类名称的代码，用于系统对窗体的管理。
	MZ_DECLARE_DYNAMIC(COViewWnd);
public:
    ImagingHelper m_ImagingHelper;
    UiCanvas m_Logo;
    UiCanvas m_Canvas;
    UiToolbar_Icon m_ToolBarIcon;
    UiToolbar_Icon m_NaviToolBarIcon;

    UiStatic m_Copyright;

    CMzString m_PicturePath;
    FilesInDir m_Pictures;
    BOOL m_SupportMove;
protected:
    int xLast, yLast;
    int xPos, yPos;
    BOOL IsFullScreen;
    BOOL IsSupportMove;
    // 初始化窗体
	virtual BOOL OnInitDialog()
	{
		// 记住在每个CMzWndEx的派生类里，只要你重写这个初始化方法，都必须要先调用下面这行代码！
		if (!CMzWndEx::OnInitDialog())
		{
			return FALSE;
		}

        // 设置主窗体标题，任务管理器及信号栏显示文字
        SetWindowText(OVIEW_APP_NAME);

        // 学习GDI画圆
        m_Logo.SetPos(GetWidth() - 160, 150, 96, 96);
        m_Logo.m_dcCanvas.Create(96, 96);
        RECT rect = {0, 0, m_Logo.m_dcCanvas.GetWidth(), m_Logo.m_dcCanvas.GetHeight()};
        HDC hDC = m_Logo.m_dcCanvas.GetDC();
        HBRUSH hBgBrush = CreateSolidBrush(MZCLR_WINDOW_BG);
        FillRect(hDC, &rect, hBgBrush);
        DeleteObject(hBgBrush);

        HPEN hPen = CreatePen(PS_NULL, 1, RGB(0, 0, 0));
        HBRUSH hBrush = CreateSolidBrush(RGB(0xE7, 0x44, 0x89));

        HGDIOBJ hOldPen = SelectObject(hDC, hPen);
        HGDIOBJ hOldBrush  = SelectObject(hDC, hBrush);

        Ellipse(hDC, 0, 0, 70, 70);

        hBrush = CreateSolidBrush(RGB(0xFF, 0xFF, 0xFF));
        SelectObject(hDC, hBrush);
        Ellipse(hDC, 10, 20, 40, 50);
        Ellipse(hDC, 45, 27, 61, 43);

        SelectObject(hDC, hOldPen);
        SelectObject(hDC, hOldBrush);
        DeleteObject(hPen);
        DeleteObject(hBrush);
        AddUiWin(&m_Logo);

        m_Copyright.SetPos(60, 120, GetWidth() - 120, 180);
        m_Copyright.SetText(L"oView v0.1\n"
                    L"http://code.google.com/p/m8tools/\n\n"
                    L"o -- Only View(只做查看)\n"
                    L"o -- Original Size(原始大小)\n"
                    L"o -- Open Source(开放源码)\n\n"
                    L"长按下方工具条中间切换全屏，点击上方工具条中间调用系统自带图片浏览器查看。全屏时点击左上角、右上角及右下角同样起作用。");
        m_Copyright.SetTextSize(16);
        m_Copyright.SetDrawTextFormat(DT_LEFT|DT_WORDBREAK|DT_END_ELLIPSIS);
        AddUiWin(&m_Copyright); // INI文件找不到最后浏览图片时才能显示，找到的话直接被m_Canvas覆盖了

        m_Canvas.SetID(MZ_IDC_OVIEW_CANVAS);
        m_Canvas.EnableNotifyMessage(true);
        AddUiWin(&m_Canvas);

        if (!m_PicturePath.IsEmpty())
        {
            GetDirectoryContents(m_PicturePath.SubStr(0, int(strrchrW(m_PicturePath.C_Str(), L'\\') - m_PicturePath.C_Str()) + 1) + L"*.*");
            LoadPicture();
        }

        // 初始化Icon toolbar
        m_ToolBarIcon.SetID(MZ_IDC_TOOLBARICON);
        m_ToolBarIcon.SetIconBarType(ICON_TOOLBAR_TYPE_NONE);
        m_ToolBarIcon.SetButtonCount(4);
        m_ToolBarIcon.LoadButtonIcon(0, MzGetInstanceHandle(), RT_RCDATA, MAKEINTRESOURCE(IDR_RCDATA_LIST));
        m_ToolBarIcon.LoadButtonIcon(3, MzGetInstanceHandle(), RT_RCDATA, MAKEINTRESOURCE(IDR_RCDATA_ROTATE));
        m_ToolBarIcon.SetHighLightButton(-1);
        m_ToolBarIcon.EnableNotifyMessage(true);
        m_ToolBarIcon.ShowButton(1, false);
        m_ToolBarIcon.ShowButton(2, false);
        AddUiWin(&m_ToolBarIcon);

        // 初始化Icon toolbar
        m_NaviToolBarIcon.SetID(MZ_IDC_TOOLBARICON_NAVI);
        m_NaviToolBarIcon.SetIconBarType(ICON_TOOLBAR_TYPE_NONE);
        m_NaviToolBarIcon.SetButtonCount(4);
        m_NaviToolBarIcon.LoadButtonIcon(0, MzGetInstanceHandle(), RT_RCDATA, MAKEINTRESOURCE(IDR_RCDATA_PREV));
        m_NaviToolBarIcon.LoadButtonIcon(3, MzGetInstanceHandle(), RT_RCDATA, MAKEINTRESOURCE(IDR_RCDATA_NEXT));
        m_NaviToolBarIcon.SetHighLightButton(-1);
        m_NaviToolBarIcon.EnableNotifyMessage(true);
        m_NaviToolBarIcon.ShowButton(1, false);
        m_NaviToolBarIcon.ShowButton(2, false);
        AddUiWin(&m_NaviToolBarIcon);

		return TRUE;
	}

    // override the MZFC command handler
    virtual void OnMzCommand(WPARAM wParam, LPARAM lParam)
    {
        UINT_PTR id = LOWORD(wParam);
        switch(id)
        {
        case MZ_IDC_TOOLBARICON:
            {
                int nIndex = lParam;

                if (nIndex == 0)
                {
                    if (AppRotation == SCREEN_ROTATION_90)
                    {
                        // 横屏时选择文件显示不全，暂时屏蔽
                    }
                    else
                    {
                        SelectPicture();
                    }
                }
                else if (nIndex == 3)
                {
                    Rotate();
                }
     
                m_ToolBarIcon.SetHighLightButton(-1);
                m_ToolBarIcon.Invalidate();
            }
            break;
        case MZ_IDC_TOOLBARICON_NAVI:
            {
                int nIndex = lParam;
   
                if (nIndex == 0)
                {
                    ViewPrev();
                }
                else if (nIndex == 3)
                {
                    ViewNext();
                }
     
                m_NaviToolBarIcon.SetHighLightButton(-1);
                m_NaviToolBarIcon.Invalidate();
            }
            break;
        }
    }

    LRESULT MzDefWndProc(UINT message, WPARAM wParam, LPARAM lParam)
    {
        switch(message)
        {
        case WM_SETTINGCHANGE:
            if (wParam == SPI_SETWORKAREA) { // Work area change, for rotation
                if (IsFullScreen)
                {
                    int width = GetSystemMetrics(SM_CXSCREEN);
                    int height = GetSystemMetrics(SM_CYSCREEN);
                    SetWindowPos(HWND_NOTOPMOST, 0, 0, width , height , SWP_SHOWWINDOW);
                }
                else
                {
                    RECT rc = MzGetWorkArea();
                    int width = RECT_WIDTH(rc);
                    int height = RECT_HEIGHT(rc);
                    SetWindowPos(HWND_NOTOPMOST , rc.left , rc.top , width , height , SWP_SHOWWINDOW);
                }
                SetDefaultPos();
                RefreshPicture();
            }
            break;
        case MZ_WM_MOUSE_NOTIFY:
            {
                int nID = LOWORD(wParam);
                int nNotify = HIWORD(wParam);
                int x = LOWORD(lParam);
                int y = HIWORD(lParam);

                switch(nID)
                {
                case MZ_IDC_TOOLBARICON_NAVI:
                    {
                        if (nNotify == MZ_MN_LBUTTONDOWN)
                        {
                            if (IsFullScreen)
                            {
                                if (x <= 90)
                                {
                                    ViewPrev();
                                }
                                else if (GetWidth() - x <= 90)
                                {
                                    ViewNext();
                                }
                            }
                            else if (abs(GetWidth() / 2 - x) <= 50)
                            {
                                SystemView();
                            }
                            
                        }

                        m_NaviToolBarIcon.ReleaseCapture();
                    }
                    break;

                case MZ_IDC_TOOLBARICON:
                    {
                        if (nNotify == MZ_MN_LBUTTONDOWN)
                        {
                            if (IsFullScreen)
                            {
                                if (GetWidth() - x <= 90)
                                {
                                    Rotate();
                                }
                            }
                            // 全屏及非全屏时都允许切换
                            if (abs(GetWidth() / 2 - x) <= 50)
                            {
                                SetTimer(m_hWnd, TIMER_FULLSCREEN_PRESSED, 500, NULL);
                            }
                        }
                        else if (nNotify == MZ_MN_LBUTTONUP)
                        {
                            KillTimer(m_hWnd, TIMER_FULLSCREEN_PRESSED);
                        }
                        
                        m_ToolBarIcon.ReleaseCapture();
                    }
                    break;

                case MZ_IDC_OVIEW_CANVAS:
                    {
                        switch (nNotify) 
                        {
                        case MZ_MN_LBUTTONDOWN:
                            {
                                xLast = x;
                                yLast = y;
                            }
                            break;
                        case MZ_MN_MOUSEMOVE:
                            {
                                if (IsSupportMove)
                                {
                                    xPos += xLast - x;
                                    yPos += yLast - y;

                                    RefreshPicture();
                                }
                                xLast = x;
                                yLast = y;
                            }
                            break;
                        }

                        m_Canvas.ReleaseCapture();
                    }
                    break;
                }
            }
            break;
        }
        return CMzWndEx::MzDefWndProc(message, wParam, lParam);
    }

    void OnTimer(UINT_PTR nIDEvent)
    {
        if (nIDEvent == TIMER_FULLSCREEN_PRESSED)
        {
            ToggleFullScreen();
        }
    }

    virtual void OnSize(int nWidth, int nHeight)
    {
        CMzWndEx::OnSize(nWidth, nHeight);

        m_ToolBarIcon.SetPos(0, nHeight - MZM_HEIGHT_ICON_TOOLBAR, nWidth, MZM_HEIGHT_ICON_TOOLBAR);
        m_Canvas.SetPos(0, 0, nWidth, nHeight);
        m_NaviToolBarIcon.SetPos(0, 0, nWidth, MZM_HEIGHT_ICON_TOOLBAR);

        Invalidate();
    }

private:
    void SystemView()
    {
        // 9.0.5 SDK的模拟器上面无法测试，只能在真机测试
        IMzOpen *pOpen = NULL;	
        IMzSysFile *pSysFile = NULL;
        CoInitializeEx(NULL, COINIT_MULTITHREADED );
        if ( SUCCEEDED( CoCreateInstance( CLSID_PhotoViewFileAssociate, NULL, CLSCTX_INPROC_SERVER, IID_MZ_SysFile, (void **)&pSysFile ) ) )
        {	
            if( SUCCEEDED( pSysFile->QueryInterface( IID_MZ_Open, (void**)&pOpen ) ) )
            {
                pSysFile->SetParentWnd( m_hWnd );
                pSysFile->SetName(m_PicturePath.C_Str());
                int ret = pOpen->Invoke(); // FIXME:若在系统图片浏览器删除了当前目录的图片，会引起BUG；界面切换可能会有问题，横屏也可能会有问题
                pOpen->Release();
            }
            pSysFile->Release();
        }
        CoUninitialize();
    }

    void ViewPrev()
    {
        typedef map<std::wstring, int>::const_iterator CI;
        CI p = m_Pictures.find(m_PicturePath.C_Str());
        if (p != m_Pictures.end() && --p != m_Pictures.end())
        {
            wcscpy(m_PicturePath, p->first.c_str());
            LoadPicture();
        }
    }

    void ViewNext()
    {
        typedef map<std::wstring, int>::const_iterator CI;
        CI p = m_Pictures.find(m_PicturePath.C_Str());
        if (p != m_Pictures.end() && ++p != m_Pictures.end())
        {
            wcscpy(m_PicturePath, p->first.c_str());
            LoadPicture();
        }
    }

    void Rotate()
    {
        RotateScreen(AppRotation ? SCREEN_ROTATION_90 : SCREEN_ROTATION_0);
        AppRotation = !AppRotation;
    }
   
    void ToggleFullScreen()
    {
        if (IsFullScreen) {
            ShowMzTopBar();
            RECT rc = MzGetWorkArea();
            int width = RECT_WIDTH(rc);
            int height = RECT_HEIGHT(rc);
            SetWindowPos(HWND_NOTOPMOST , rc.left , rc.top , width , height , SWP_SHOWWINDOW);
            m_ToolBarIcon.ShowButton(0, true);
            m_ToolBarIcon.ShowButton(3, true);
            m_NaviToolBarIcon.ShowButton(0, true);
            m_NaviToolBarIcon.ShowButton(3, true);
        } else {
            HideMzTopBar();
            int width = GetSystemMetrics(SM_CXSCREEN);
            int height = GetSystemMetrics(SM_CYSCREEN);
            SetWindowPos(HWND_NOTOPMOST, 0, 0, width , height , SWP_SHOWWINDOW);
            m_ToolBarIcon.ShowButton(0, false);
            m_ToolBarIcon.ShowButton(3, false);
            m_NaviToolBarIcon.ShowButton(0, false);
            m_NaviToolBarIcon.ShowButton(3, false);
        }

        IsFullScreen = !IsFullScreen;
        SetDefaultPos();
        RefreshPicture();
    }

    void RefreshPicture()
    {
        RECT rect = m_Canvas.GetClientRect();
        int nWidth = RECT_WIDTH(rect);
        int nHeight = RECT_HEIGHT(rect);
        m_Canvas.m_dcCanvas.Create(nWidth, nHeight);
   
        //int start = GetTickCount();
        BitBlt(m_Canvas.m_dcCanvas.GetDC(), 0, 0, nWidth, nHeight, m_ImagingHelper.GetDC(), xPos, yPos, SRCCOPY);

        m_Canvas.Invalidate();
        m_Canvas.Update();//这个非常重要
        //RETAILMSG(1, (L"图层绘制时间:%d！\n", GetTickCount() - start));
    }

    void SetDefaultPos()
    {
        RECT rect = m_Canvas.GetClientRect();
        int nWidth = RECT_WIDTH(rect);
        int nHeight = RECT_HEIGHT(rect);

        int nImageWidth = m_ImagingHelper.GetImageWidth();
        int nImageHeight = m_ImagingHelper.GetImageHeight();

        // 图片尺寸小于显示区的自动在显示区中间出现
        if (nImageWidth <= nWidth && nImageHeight <= nHeight)
        {
            xPos = (nImageWidth - nWidth) / 2;
            yPos = (nImageHeight - nHeight) / 2;
            IsSupportMove = false;
        }
        else
        {
            xPos = 0;
            yPos = 0;
            IsSupportMove = true;
        }
    }

    void LoadPicture()
    {
        typedef map<std::wstring, int>::const_iterator CI;
        CI p = m_Pictures.find(m_PicturePath.C_Str());
        if (p != m_Pictures.end())
        {
            // 假定大于500K时加载较慢
            if (p->second > 500 * 1024)
            {
                MzBeginWaitDlg(m_hWnd);
                m_ImagingHelper.LoadImage(m_PicturePath, true, false, false);
                MzEndWaitDlg();
            }
            else {
                m_ImagingHelper.LoadImage(m_PicturePath, true, false, false);
            }
        }

        SetDefaultPos();
        RefreshPicture();
    }

    void SelectPicture()
    {
        IMzSelect *pSelect = NULL; 
        IFileBrowser *pFile = NULL;

        CoInitializeEx(NULL, COINIT_MULTITHREADED );
        if ( SUCCEEDED( CoCreateInstance( CLSID_FileBrowser, NULL, CLSCTX_INPROC_SERVER , IID_MZ_FileBrowser, (void **)&pFile ) ) )
        {     
            if( SUCCEEDED( pFile->QueryInterface(IID_MZ_Select, (void**)&pSelect ) ))
            {
                pFile->SetParentWnd( m_hWnd );
                if (!m_PicturePath.IsEmpty())
                {
                    pFile->SetOpenDirectoryPath(m_PicturePath.SubStr(0, int(strrchrW(m_PicturePath.C_Str(), L'\\') - m_PicturePath.C_Str()))); //如果不调用此函数则默认为根目录FIXME:要记住上一次开启的目录
                }
               
                pFile->SetExtFilter( L"*.png;*.jpg;*.gif;*.bmp" );                                      
                pFile->SetOpenDocumentType(DOCUMENT_SELECT_SINGLE_FILE); //应用根据需求进行文档打开方式的设置
                if( pSelect->Invoke() ) 
                {
                    if (wcscmp(m_PicturePath.C_Str(), pFile->GetSelectedFileName()) == 0)
                    {
                        SetDefaultPos();
                        RefreshPicture();
                    }
                    else
                    {
                        wcscpy(m_PicturePath, pFile->GetSelectedFileName());
                        m_Pictures.clear();
                        GetDirectoryContents(m_PicturePath.SubStr(0, int(strrchrW(m_PicturePath.C_Str(), L'\\') - m_PicturePath.C_Str()) + 1) + L"*.*");
                        LoadPicture();
                    }
                }
                pSelect->Release();
            }     
            pFile->Release();
        }	
        CoUninitialize();
    }

    // 参考自Windows(R) CE 3.0 Application Programming
    void CollectPictures(WIN32_FIND_DATA *fdData)
    {
        // Directory and temporary means removable media
        if ((fdData->dwFileAttributes
            & FILE_ATTRIBUTE_TEMPORARY)
            && (fdData->dwFileAttributes
            & FILE_ATTRIBUTE_DIRECTORY) )
        {
        }
        // If it's a directory, print the name
        else if(fdData->dwFileAttributes
            & FILE_ATTRIBUTE_DIRECTORY)
        {
        }
        else  // it's a file, print name and size
        {
            std::wstring filename = fdData->cFileName;
            std::wstring::size_type index = filename.find_last_of(_T('.'));

            if (index != std::wstring::npos)
            {
                std::wstring ext = filename.substr(index + 1);  //取得文件的扩展名
                
                if (wcsicmp(ext.c_str(), L"png") == 0
                    || wcsicmp(ext.c_str(), L"jpg") == 0
                    || wcsicmp(ext.c_str(), L"gif") == 0
                    || wcsicmp(ext.c_str(), L"bmp") == 0)  //判断文件的扩展名是否在扩展名列表里*.png;*.jpg;*.gif;*.bmp,大小写呢？
                {
                    m_Pictures.insert(make_pair(
                        m_PicturePath.SubStr(0, int(strrchrW(m_PicturePath.C_Str(), L'\\') - m_PicturePath.C_Str()) + 1) + fdData->cFileName, 
                        fdData->nFileSizeLow));
                }

            }
        }
    }

    void GetDirectoryContents(LPTSTR lpFileMask)
    {
        CMzString msg(256);
        HANDLE hFindFile;
        WIN32_FIND_DATA fdData;
        // get first file
        hFindFile = FindFirstFile(lpFileMask, &fdData);
        if(hFindFile != INVALID_HANDLE_VALUE)
        {
            CollectPictures(&fdData);
            while(FindNextFile(hFindFile, &fdData))
            {
                CollectPictures(&fdData);
            }
            FindClose(hFindFile);
        }
        else
        {
            wsprintf(msg.C_Str(), L"Call to FindFirstFile failed:%d ", GetLastError());
            OutputDebugString(msg.C_Str());
        }
    }
};


// 同样是SDK提供的宏，自动生成一段代码。
MZ_IMPLEMENT_DYNAMIC(COViewWnd)

// 利用SDK提供的应用程序类“模板”CMzApp派生出自己的应用程序类，每个M8程序都必须
class COViewApp: public CMzApp
{
private:
    CMzString iniFile;
public:
	// 我们这个程序有一个窗体
	COViewWnd m_oViewWnd;

	// 应用程序初始化
	virtual BOOL Init()
	{
        // Init the COM relative library.
		CoInitializeEx(0, COINIT_MULTITHREADED);

        // Set App Path
        AppPath = GetModuleFileName(NULL).C_Str();
        AppPath = AppPath.SubStr(0, int(strrchrW(AppPath.C_Str(), L'\\') - AppPath.C_Str()) + 1);

        iniFile = AppPath + L"\\oView.ini";

        IniCreateFile(iniFile);

        m_oViewWnd.m_PicturePath.SetBufferSize(MAX_PATH);

        TCHAR* tmpStr;
        if (IniReadString(OVIEW_APP_NAME, L"LastPicture", &tmpStr, iniFile))
        {
            wcscpy(m_oViewWnd.m_PicturePath, tmpStr);
            delete tmpStr;
        }

        AppRotation = SCREEN_ROTATION_0;

		// 获取目前可用的屏幕区域，用于创建这个窗体，并显示出来。
		RECT rcWork = MzGetWorkArea();
		m_oViewWnd.Create(rcWork.left, rcWork.top, RECT_WIDTH(rcWork), RECT_HEIGHT(rcWork), 0, 0, 0);
		m_oViewWnd.Show();

		// 初始化成功，需要返回一个 TRUE
		return TRUE;
	}
 
    virtual int Done() {
        IniWriteString(OVIEW_APP_NAME, L"LastPicture", m_oViewWnd.m_PicturePath, iniFile);
        return TRUE;
    }
};

// 将我们派生出来的应用程序类实例化，这就是我们程序的入口了。
COViewApp oViewApp;